在日常生活中,我們經常會去咖啡店買咖啡,但如果想要來點變化,像是加一點牛奶或糖,就可以讓平凡的咖啡多一點風味。同樣地,在程式設計中,我們有時也需要在不改變原有功能的前提下,為某些物件「加點料」。這就是裝飾者模式 Decorator Pattern 所要解決的問題。
裝飾者模式是一種結構型設計模式,它允許你動態地為物件添加功能,而不需要修改其原有的程式碼。換句話說,裝飾者模式讓你可以根據不同的需求,隨時為一個物件「增添佐料」,就像你為咖啡加牛奶或糖一樣,都會改變咖啡的口感和價格,但我們並沒有改變咖啡本身。
讓我們用一個簡單的例子來說明。假設我們有一個基本的咖啡類別 Coffee
,它代表一杯不加任何配料的黑咖啡。你可以根據需求選擇加入牛奶、糖或其他配料。使用裝飾者模式,我們可以輕鬆地計算出各種組合的價格,而不需要為每種可能的組合建立新的類別。
首先,我們定義一個 Coffee
類別,並為它計算基本價錢的功能:
class Coffee {
public:
virtual std::string getDescription() const {
return "黑咖啡";
}
virtual int cost() const {
return 50;
}
virtual ~Coffee() = default;
};
假設你想在咖啡中加牛奶,我們可以建立一個裝飾者類別 MilkDecorator
,這個類別將會裝飾(也就是包裝)Coffee
類別,並在其基礎上增加牛奶的功能:
class MilkDecorator : public Coffee {
private:
std::unique_ptr<Coffee> coffee;
public:
MilkDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
std::string getDescription() const override {
return coffee->getDescription() + "+牛奶";
}
int cost() const override {
return coffee->cost() + 15;
}
};
如果你想再加點糖,可以再建立一個 SugarDecorator
類別:
class SugarDecorator : public Coffee {
private:
std::unique_ptr<Coffee> coffee;
public:
SugarDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
std::string getDescription() const override {
return coffee->getDescription() + "+糖";
}
int cost() const override {
return coffee->cost() + 5;
}
};
最後我們就可以根據自己的需求自由組合這些裝飾者:
int main() {
std::unique_ptr<Coffee> coffee = std::make_unique<Coffee>();
std::cout << coffee->getDescription() << "價格:"
<< coffee->cost() << "元\n";
coffee = std::make_unique<MilkDecorator>(std::move(coffee));
std::cout << coffee->getDescription() << "價格:"
<< coffee->cost() << "元\n";
coffee = std::make_unique<SugarDecorator>(std::move(coffee));
std::cout << coffee->getDescription() << "價格:"
<< coffee->cost() << "元\n";
return 0;
}
執行上述程式碼,我們會得到以下輸出:
黑咖啡價格:50元
黑咖啡+牛奶價格:65元
黑咖啡+牛奶+糖價格:70元
在這個範例中,我們從一杯基本的黑咖啡開始,然後動態地添加牛奶和糖,最終計算出一杯加了牛奶和糖的咖啡價格。每次添加新配料時,我們不需要修改現有的咖啡類別,只需透過裝飾者來實現功能的擴展。這樣的設計讓我們可以靈活地擴展功能,也讓程式碼更加易於維護。
裝飾者模式的最大優點就是靈活性,你可以任意組合裝飾者成各種不同的組合。當你面對需要不斷添加新功能的情況時,使用裝飾者模式可以避免類別數量的膨脹,讓程式碼更易於維護和擴展。同時它也保持了開放封閉原則,也就是添加新的裝飾者不需要修改原始類別,也符合單一職責原則,即每個裝飾者只負責一項特定的功能增強。
裝飾者模式就像是在咖啡裡添加牛奶和糖,讓原本簡單的東西變得更加豐富多樣。在程式設計中,它能幫助我們以一種靈活的方式,擴展物件的功能,同時保持程式碼的清晰與可維護性。下次當你面臨需要擴展物件功能的需求時,考慮一下裝飾者模式,它或許就是你需要的解決方案!
更多C++語言相關的文章,歡迎追蹤我的部落格
https://shengyu7697.github.io/cpp-decorator-pattern/